#include "Logger.h"
#include <cstddef>
#include <cstdint>
#include <cstdio>
#include <cstring>
#include <memory>
#include <mutex>
#include <strings.h>
#if defined(__CYGWIN__)
#    include <functional>
#    include <sys/cygwin.h>
#    include <thread>
#else
#    include <sys/syscall.h>  //system call
#endif
#include <sys/types.h>  //getpid, gettid
#include <unistd.h>
#include "AsyncLogQueue.h"
#include "Common.h"
#include "LoggerEngine.h"
#include "LoggerFile.h"
#if __cplusplus >= 201402L
#    include "LoggerNew.h"
#endif

namespace xncc {
namespace {
    __thread AsyncLogQueue* ptr_              = nullptr;
    __thread thread_id_type cache_tid         = 0;
    uint16_t                __loggerIdInitVal = 0;

}  // namespace
struct QueueDestoryFLag
{
    void QueueDestoryFLagInit()
    {
    }
    ~QueueDestoryFLag()
    {
        if (ptr_ != nullptr) {
            ptr_->setCanDestory();
        }
    }
};
namespace {
    auto getLoggerEngine() -> LoggerEngine&
    {
        static LoggerEngine logengine;
        return logengine;
    }

    auto gettid() -> thread_id_type
    {
        if (UNLIKELY(cache_tid == 0)) {  // 不太可能的情况 发生了
#if defined(__CYGWIN__)
            cache_tid = static_cast<thread_id_type>(std::hash<std::thread::id>{}(std::this_thread::get_id()));
#else
            cache_tid = syscall(__NR_gettid);
#endif
        }
        return cache_tid;
    }

    thread_local QueueDestoryFLag queueFlag;

}  // namespace

Logger __console_log("stdout");

Logger::Logger() : logLevel_(xncc::LOG_LEVEL::DEBUG), loggerId_(++__loggerIdInitVal), is_init_(false)
{
}

Logger::Logger(const std::string& logName, const std::string& logPath, int coreId)
    : logLevel_(xncc::LOG_LEVEL::DEBUG), loggerId_(++__loggerIdInitVal), is_init_(true)
{
    getLoggerEngine().addLogFile(
        std::make_shared<xncc::LoggerFile>(
            LoggerConfig{logName, logPath, 0, LOG_LEVEL::DEBUG, strncasecmp(logName.c_str(), "STDOUT", 6) == 0, true}),
        loggerId_, coreId);
}

Logger::Logger(const std::string& logName,
               LOG_LEVEL          logLevel,
               const std::string& logPath,
               std::size_t        rotateSize,
               int                core,
               bool               nameFixed)
    : logLevel_(logLevel), loggerId_(++__loggerIdInitVal), is_init_(true)
{
    getLoggerEngine().addLogFile(
        std::make_shared<xncc::LoggerFile>(LoggerConfig{logName, logPath, rotateSize, logLevel,
                                                        strncasecmp(logName.c_str(), "STDOUT", 6) == 0, nameFixed}),
        loggerId_, core);
}

void Logger::Set(const std::string& logName,
                 const std::string& logPath,
                 std::size_t        rotateSize,
                 LOG_LEVEL          logLevel,
                 int                coreId,
                 bool               nameFixed)
{
    if (is_init_) {
        TRACE_DEBUG("logName:%s logPath:%s has inited Set invalid", logName.c_str(), logPath.c_str());
        return;
    }
    logLevel_ = logLevel;
    getLoggerEngine().addLogFile(
        std::make_shared<xncc::LoggerFile>(LoggerConfig{logName, logPath, rotateSize, logLevel,
                                                        strncasecmp(logName.c_str(), "STDOUT", 6) == 0, nameFixed}),
        loggerId_, coreId);
    is_init_ = true;
}

void Logger::start()
{
    static std::once_flag once_flag;
    std::call_once(once_flag, [capture0 = &getLoggerEngine()] { capture0->start(); });
}

static constexpr size_t   DATA_LEN_FLAG_LEN = sizeof(uint32_t);
static constexpr size_t   DATA_TYEPE_LEN    = 1;
static constexpr uint32_t HEADER_LEN        = sizeof(uint32_t);
static constexpr uint32_t FMTID_LEN         = sizeof(int32_t);

#if __cplusplus >= 201402L

LogNewObject::LogNewObject(std::size_t            argsSize,
                           int32_t&               fmtId,
                           const ParamStaticInfo* params,
                           xncc::LOG_LEVEL        level,
                           const char*            file,
                           uint32_t               line,
                           const char*            function,
                           uint16_t               loggerId)
{
    const uint64_t bufferSize = HEADER_LEN + DATA_TYEPE_LEN + LogHeaderV2::HEADER_LEN() + argsSize + DATA_TYEPE_LEN;
    if (UNLIKELY(ptr_ == nullptr)) {
        ptr_ = getLoggerEngine().createAsyncQueue();
        queueFlag.QueueDestoryFLagInit();
    }
    if (UNLIKELY(fmtId == UNKNOWNED_FMT_ID)) {
        fmtId = getLoggerEngine().storeParamsInfo(params);
    }
    logBuf_                                        = ptr_->getWriteBuffer(bufferSize);  // 自动扩容 不会返回空指针
    *reinterpret_cast<uint32_t*>(logBuf_)          = static_cast<uint32_t>(bufferSize);
    offset_                                        = HEADER_LEN;
    *reinterpret_cast<uint8_t*>(logBuf_ + offset_) = static_cast<uint8_t>(xncc::LOG_PARAM_TYPE::HEADER_V2_TYPE);
    offset_ += DATA_TYEPE_LEN;
    auto* loggerHeader =
        new (logBuf_ + offset_) xncc::LogHeaderV2(level, xncc::gettid(), file, line, function, loggerId, fmtId);
    (void)loggerHeader;
    offset_ += sizeof(xncc::LogHeaderV2);
}

LogNewObject::LogNewObject(std::size_t argsSize, int32_t& fmtId, const ParamStaticInfo* params, uint16_t loggerId)
{
    const uint64_t bufferSize = HEADER_LEN + DATA_TYEPE_LEN + LogHeaderV3::HEADER_LEN() + argsSize + DATA_TYEPE_LEN;
    if (UNLIKELY(ptr_ == nullptr)) {
        ptr_ = getLoggerEngine().createAsyncQueue();
        queueFlag.QueueDestoryFLagInit();
    }
    if (UNLIKELY(fmtId == UNKNOWNED_FMT_ID)) {
        fmtId = getLoggerEngine().storeParamsInfo(params);
    }
    logBuf_                                        = ptr_->getWriteBuffer(bufferSize);  // 自动扩容 不会返回空指针
    *reinterpret_cast<uint32_t*>(logBuf_)          = static_cast<uint32_t>(bufferSize);
    offset_                                        = HEADER_LEN;
    *reinterpret_cast<uint8_t*>(logBuf_ + offset_) = static_cast<uint8_t>(xncc::LOG_PARAM_TYPE::HEADER_V3_TYPE);
    offset_ += DATA_TYEPE_LEN;
    auto* loggerHeader = new (logBuf_ + offset_) xncc::LogHeaderV3(loggerId, fmtId);
    (void)loggerHeader;
    offset_ += sizeof(xncc::LogHeaderV3);
}

LogNewObject::~LogNewObject()
{
    *reinterpret_cast<uint8_t*>(logBuf_ + offset_) = static_cast<uint8_t>(xncc::LOG_PARAM_TYPE::OVER_FLAG_TYPE);
    offset_ += DATA_TYEPE_LEN;
    ptr_->finishWrite();
}

LogNewBinary::LogNewBinary(std::size_t argsSize, uint16_t loggerId)
{
    const uint64_t bufferSize = HEADER_LEN + DATA_TYEPE_LEN + LogHeaderV6::HEADER_LEN() + argsSize;
    if (UNLIKELY(ptr_ == nullptr)) {
        ptr_ = getLoggerEngine().createAsyncQueue();
        queueFlag.QueueDestoryFLagInit();
    }
    logBuf_                                        = ptr_->getWriteBuffer(bufferSize);  // 自动扩容 不会返回空指针
    *reinterpret_cast<uint32_t*>(logBuf_)          = static_cast<uint32_t>(bufferSize);
    offset_                                        = HEADER_LEN;
    *reinterpret_cast<uint8_t*>(logBuf_ + offset_) = static_cast<uint8_t>(xncc::LOG_PARAM_TYPE::HEADER_V6_TYPE);
    offset_ += DATA_TYEPE_LEN;
    auto loggerHeader = new (logBuf_ + offset_) xncc::LogHeaderV6(loggerId, -1);
    (void)loggerHeader;
    offset_ += sizeof(xncc::LogHeaderV6);
}

LogNewBinary::~LogNewBinary()
{
    ptr_->finishWrite();
}

#endif

Logbinary::Logbinary(uint16_t loggerId) : offset_(HEADER_LEN), logBuf_(nullptr), curBufLen_(512)
{
    if (UNLIKELY(ptr_ == nullptr)) {
        ptr_ = getLoggerEngine().createAsyncQueue();
        queueFlag.QueueDestoryFLagInit();
    }
    logBuf_                                        = ptr_->getWriteBuffer(curBufLen_);  // 自动扩容 不会返回空指针
    *reinterpret_cast<uint8_t*>(logBuf_ + offset_) = static_cast<uint8_t>(LOG_PARAM_TYPE::HEADER_V5_TYPE);
    offset_ += DATA_TYEPE_LEN;
    auto* loggerHeader = new (logBuf_ + offset_) LogHeaderV5(loggerId);
    (void)loggerHeader;
    offset_ += sizeof(LogHeaderV5);
}
Logbinary::~Logbinary()
{
    auto* bodyLen    = reinterpret_cast<uint32_t*>(logBuf_);
    *bodyLen         = static_cast<uint32_t>(curBufLen_);
    auto* header     = reinterpret_cast<LogHeaderV5*>(logBuf_ + HEADER_LEN + DATA_TYEPE_LEN);
    header->dataSize = static_cast<uint32_t>(offset_ - DATA_TYEPE_LEN - HEADER_LEN - sizeof(LogHeaderV5));
    ptr_->finishWrite();
}

namespace {
    std::size_t CalcEnoughLen(std::size_t expectedLen, std::size_t curLen)
    {
        while (curLen < expectedLen) {
            curLen *= 2;
        }
        return curLen;
    }
}  // namespace

void Logbinary::ensureLogbufEnough(const size_t incLen)
{
    char* originLogbuf = logBuf_;
    (void)originLogbuf;
    if (UNLIKELY(offset_ + incLen > curBufLen_)) {  // 说明内存不够
        curBufLen_      = CalcEnoughLen(curBufLen_ + incLen, curBufLen_);
        char* expectBuf = ptr_->tryGetWriteBuffer(curBufLen_);  // 当前buffer不够写了
        if (UNLIKELY(expectBuf == nullptr)) {                   // 需要扩容
            char* tmpBuf = new char[offset_];
            memcpy(tmpBuf, logBuf_, offset_);
            logBuf_ = ptr_->getWriteBuffer(curBufLen_);
            assert(originLogbuf != logBuf_);
            memcpy(logBuf_, tmpBuf, offset_);
            delete[] tmpBuf;
        }
    }
}

void Logbinary::extendOffset(std::size_t entrySize)
{
    offset_ += entrySize;
}

auto Logbinary::get(std::size_t entrySize) -> char*
{
    ensureLogbufEnough(entrySize);
    return logBuf_ + offset_;
}

// |type|frame|len|str|
// |type|frame|num|
LogObject::LogObject(LOG_LEVEL level, const char* file, uint32_t line, const char* function, uint16_t loggerId)
    : offset_(HEADER_LEN), logBuf_(nullptr), curBufLen_(256), isFlushed_(false), isBoolalpha_(false)
{
    if (UNLIKELY(ptr_ == nullptr)) {
        ptr_ = getLoggerEngine().createAsyncQueue();
        queueFlag.QueueDestoryFLagInit();
    }
    logBuf_                                        = ptr_->getWriteBuffer(curBufLen_);  // 自动扩容 不会返回空指针
    *reinterpret_cast<uint8_t*>(logBuf_ + offset_) = static_cast<uint8_t>(LOG_PARAM_TYPE::HEADER_V1_TYPE);
    offset_ += DATA_TYEPE_LEN;
    auto* loggerHeader = new (logBuf_ + offset_) LogHeaderV1(level, gettid(), file, line, function, loggerId);
    (void)loggerHeader;
    offset_ += sizeof(LogHeaderV1);
}

LogObject::LogObject(uint16_t loggerId) : offset_(HEADER_LEN), logBuf_(nullptr), curBufLen_(256), isFlushed_(false)
{
    if (UNLIKELY(ptr_ == nullptr)) {
        ptr_ = getLoggerEngine().createAsyncQueue();
        queueFlag.QueueDestoryFLagInit();
    }
    logBuf_                                        = ptr_->getWriteBuffer(curBufLen_);  // 自动扩容 不会返回空指针
    *reinterpret_cast<uint8_t*>(logBuf_ + offset_) = static_cast<uint8_t>(LOG_PARAM_TYPE::HEADER_V4_TYPE);
    offset_ += DATA_TYEPE_LEN;
    auto* loggerHeader = new (logBuf_ + offset_) LogHeaderV4(loggerId);
    (void)loggerHeader;
    offset_ += sizeof(LogHeaderV4);
}

LogObject::~LogObject()
{
    if (!isFlushed_) {
        flush();
    }
}

void LogObject::ensureLogbufEnough(const size_t incLen)
{
    char* originLogbuf = logBuf_;
    (void)originLogbuf;
    if (UNLIKELY(offset_ + incLen > curBufLen_)) {  // 说明内存不够
        curBufLen_      = CalcEnoughLen(curBufLen_ + incLen, curBufLen_);
        char* expectBuf = ptr_->tryGetWriteBuffer(curBufLen_);  // 当前buffer不够写了
        if (UNLIKELY(expectBuf == nullptr)) {                   // 需要扩容
            char* tmpBuf = new char[offset_];
            memcpy(tmpBuf, logBuf_, offset_);
            logBuf_ = ptr_->getWriteBuffer(curBufLen_);
            assert(originLogbuf != logBuf_);
            memcpy(logBuf_, tmpBuf, offset_);
            delete[] tmpBuf;
        }
    }
}
template <typename T>
void LogObject::encodeNumParam(const T data, uint8_t pType)
{
    // 编码前先看内存够不够
    size_t incLen = DATA_TYEPE_LEN + sizeof(T);  // 需要新增的长度
    ensureLogbufEnough(incLen);
    *reinterpret_cast<uint8_t*>(logBuf_ + offset_) = pType;
    offset_ += DATA_TYEPE_LEN;
    *reinterpret_cast<T*>(logBuf_ + offset_) = data;
    offset_ += sizeof(T);
}

void LogObject::encodeStrParam(const char* str, uint8_t pType, std::size_t str_len)
{
    // 编码前先看内存够不够
    size_t incLen = DATA_TYEPE_LEN + DATA_LEN_FLAG_LEN + str_len;  // 需要新增的长度
    ensureLogbufEnough(incLen);
    *reinterpret_cast<uint8_t*>(logBuf_ + offset_) = static_cast<uint8_t>(pType);
    offset_ += DATA_TYEPE_LEN;
    *reinterpret_cast<uint32_t*>(logBuf_ + offset_) = static_cast<uint32_t>(str_len);
    offset_ += DATA_LEN_FLAG_LEN;
    memcpy(logBuf_ + offset_, str, str_len);
    offset_ += str_len;
}

auto LogObject::operator<<(bool data) -> LogObject&
{
    if (isBoolalpha_) {
        *this << (data ? "true" : "false");
    }
    else {
        *this << (data ? "1" : "0");
    }
    return *this;
}

auto LogObject::operator<<(StdIoBaseFlag flag) -> LogObject&
{
    if (flag == std::boolalpha) {
        isBoolalpha_ = true;
    }
    else if (flag == std::noboolalpha) {
        isBoolalpha_ = false;
    }
    else if (flag == std::dec) {
        addFlag(static_cast<uint8_t>(LOG_PARAM_TYPE::DEC_FLAG_TYPE));
    }
    else if (flag == std::hex) {
        addFlag(static_cast<uint8_t>(LOG_PARAM_TYPE::HEX_FLAG_TYPE));
    }
    else if (flag == std::oct) {
        addFlag(static_cast<uint8_t>(LOG_PARAM_TYPE::OCT_FLAG_TYPE));
    }
    else if (flag == std::binary) {
        addFlag(static_cast<uint8_t>(LOG_PARAM_TYPE::BIN_FLAG_TYPE));
    }
    else {
        TRACE_DEBUG("unsupported iobase");
    }
    return *this;
}

auto LogObject::operator<<(char data) -> LogObject&
{
    encodeStrParam(&data, static_cast<uint8_t>(LOG_PARAM_TYPE::CHAR_PTR_TYPE), 1);
    return *this;
}

auto LogObject::operator<<(short data) -> LogObject&
{
    encodeNumParam(data, static_cast<uint8_t>(LOG_PARAM_TYPE::SHORT_TYPE));
    return *this;
}
auto LogObject::operator<<(unsigned short data) -> LogObject&
{
    encodeNumParam(data, static_cast<uint8_t>(LOG_PARAM_TYPE::UNSIGED_SHORT_TYPE));
    return *this;
}
auto LogObject::operator<<(int data) -> LogObject&
{
    encodeNumParam(data, static_cast<uint8_t>(LOG_PARAM_TYPE::INT_TYPE));
    return *this;
}
auto LogObject::operator<<(unsigned int data) -> LogObject&
{
    encodeNumParam(data, static_cast<uint8_t>(LOG_PARAM_TYPE::UNSIGNED_INT_TYPE));
    return *this;
}
auto LogObject::operator<<(long data) -> LogObject&
{
    encodeNumParam(data, static_cast<uint8_t>(LOG_PARAM_TYPE::LONG_TYPE));
    return *this;
}
auto LogObject::operator<<(unsigned long data) -> LogObject&
{
    encodeNumParam(data, static_cast<uint8_t>(LOG_PARAM_TYPE::UNSIGNED_LONG_TYPE));
    return *this;
}
auto LogObject::operator<<(long long data) -> LogObject&
{
    encodeNumParam(data, static_cast<uint8_t>(LOG_PARAM_TYPE::LONG_LONG_TYPE));
    return *this;
}
auto LogObject::operator<<(unsigned long long data) -> LogObject&
{
    encodeNumParam(data, static_cast<uint8_t>(LOG_PARAM_TYPE::UNSIGNED_LONG_LONG_TYPE));
    return *this;
}

auto LogObject::operator<<(const void* data) -> LogObject&
{
    auto v = reinterpret_cast<uintptr_t>(data);
    encodeNumParam(v, static_cast<uint8_t>(LOG_PARAM_TYPE::PTR_TYPE));
    return *this;
}
auto LogObject::operator<<(float data) -> LogObject&
{
    *this << static_cast<double>(data);
    return *this;
}
auto LogObject::operator<<(double data) -> LogObject&
{
    encodeNumParam(data, static_cast<uint8_t>(LOG_PARAM_TYPE::DOUBLE_TYPE));
    return *this;
}

auto LogObject::operator<<(const char* data) -> LogObject&
{
    const std::size_t str_len = __builtin_strlen(data);
    encodeStrParam(data, static_cast<uint8_t>(LOG_PARAM_TYPE::CHAR_PTR_TYPE), str_len);
    return *this;
}
auto LogObject::operator<<(const unsigned char* str) -> LogObject&
{
    const char*       data    = reinterpret_cast<const char*>(str);
    const std::size_t str_len = __builtin_strlen(data);
    encodeStrParam(data, static_cast<uint8_t>(LOG_PARAM_TYPE::CHAR_PTR_TYPE), str_len);
    return *this;
}

auto LogObject::operator<<(StaticString str) -> LogObject&
{
    encodeNumParam(reinterpret_cast<uintptr_t>(str.data), static_cast<uint8_t>(LOG_PARAM_TYPE::CONST_CHAR_PTR_TYPE));
    return *this;
}

auto LogObject::operator<<(const std::string& str) -> LogObject&
{
    encodeStrParam(str.c_str(), static_cast<uint8_t>(LOG_PARAM_TYPE::CHAR_PTR_TYPE), str.size());
    return *this;
}
void LogObject::operator<<(void (*fp)(LogObject&))
{
    fp(*this);
}

void LogObject::extendOffset(std::size_t entrySize)
{
    *reinterpret_cast<uint8_t*>(logBuf_ + offset_) = static_cast<uint8_t>(LOG_PARAM_TYPE::LOG_OBJ_TYPE);
    offset_ += DATA_TYEPE_LEN;
    *reinterpret_cast<uint32_t*>(logBuf_ + offset_) = static_cast<uint32_t>(entrySize);
    offset_ += DATA_LEN_FLAG_LEN + entrySize;
}

auto LogObject::get(std::size_t entrySize) -> char*
{
    // 编码前先看内存够不够
    size_t incLen = DATA_TYEPE_LEN + entrySize;  // 需要新增的长度
    ensureLogbufEnough(incLen);
    return logBuf_ + offset_ + DATA_TYEPE_LEN + DATA_LEN_FLAG_LEN;
}

void LogObject::flush()
{
    addFlag(static_cast<uint8_t>(LOG_PARAM_TYPE::OVER_FLAG_TYPE));
    auto* bodyLen = reinterpret_cast<uint32_t*>(logBuf_);
    *bodyLen      = static_cast<uint32_t>(curBufLen_);
    ptr_->finishWrite();
    isFlushed_ = true;
}

void LogObject::addFlag(uint8_t flag)
{
    ensureLogbufEnough(DATA_TYEPE_LEN);
    *reinterpret_cast<uint8_t*>(logBuf_ + offset_) = flag;
    offset_ += DATA_TYEPE_LEN;
}

void endl(LogObject& fileline)
{
    fileline.flush();
}

}  // namespace xncc